#include "UIView.h"
#include "../Events/EventManager.h"
#include "../Input/Mouse.h"
#include "../Input/Keyboard.hpp"
#include "UIWindow.h"
#include <thread>
#include <iostream>

typedef void(*keybd_callback_function)(UIElement*, KeyEvt);
typedef void(UIView::*ui_view_keyevt_callback)(int key, int state);
std::map<UIView*, ui_view_keyevt_callback> _uiview_keyevt_callbacks;

void __mc_RegisterUIViewKeyEvtsListener(ui_view_keyevt_callback cb, UIView* this_ptr)
{
	_uiview_keyevt_callbacks[this_ptr] = cb;
}

void __mc_Synchronized_Keyboard_Polling()
{
	while (Keyboard::IsInitialized())
	{
		Sleep(4);
		Keyboard::PollEvents();
		for (int key = 1; key <= 74; key++)
		{
			int state = Keyboard::GetKeyState(key);
			if (state != MC_NOKEY)
				for (const auto& pair : _uiview_keyevt_callbacks)
					(pair.first->*pair.second)(key, state);
		}
	}
}

UIView::UIView()
{
	// initial keyboard handling initialization
	if (!Keyboard::IsInitialized())
	{
		Keyboard::Initialize();
		std::thread keyboardEventListenerThread(__mc_Synchronized_Keyboard_Polling); // keyboard state
		keyboardEventListenerThread.detach();
	}

	this->eventManager = new EventManager(this);
	std::thread mouseClickEventListenerThread(&UIView::MouseClickEventListener, this); // mouse clicks
	mouseClickEventListenerThread.detach();

	std::thread mouseMovementEventListenerThread(&UIView::MouseMovementEventListener, this); // mouse movement
	mouseMovementEventListenerThread.detach();

	std::thread mousePressedEventListenerThread(&UIView::MousePressedEventListener, this); // mouse pressed state
	mousePressedEventListenerThread.detach();

	__mc_RegisterUIViewKeyEvtsListener(&UIView::ProcessKeyEvents, this);
}

void UIView::Add(UIElement* element)
{
	this->uiElements.push_back(element);
}

void UIView::Remove(UIElement* element)
{
	for (int i = 0; i < (int)uiElements.size(); i++)
	{
		if (this->uiElements.at(i) == element)
		{
			this->uiElements.erase(uiElements.begin() + (i));
		}
	}
}

void UIView::RemoveAllElements()
{
	this->uiElements.clear();
}

int UIView::IndexOf(UIElement* elem)
{
	size_t index = std::find(uiElements.begin(), uiElements.end(), elem) - uiElements.begin();
	if (index >= uiElements.size()) return -1;
	return (int)index;
}

void UIView::DepthSort()
{
	int depth = 0;
	for (UIElement* elem : uiElements)
	{
		elem->DepthIndex = depth;
		depth++;
	}
	topMostPositionZ = depth;
}

void UIView::SetTopMostElement(UIElement* element)
{
	int currentIndex = IndexOf(element);
	if (currentIndex != -1)
	{
		this->uiElements.erase(this->uiElements.begin() + (currentIndex));
	}

	this->uiElements.push_back(element);

	DepthSort();
}

void UIView::Terminate()
{
	this->_should_terminate = true;
	while (!(
		_mouse_click_event_listener_terminated &&
		_mouse_movement_event_listener_terminated &&
		_mouse_pressed_event_listener_terminated
		))
	{
		Sleep(12);
	}
}

UIView::~UIView()
{
	for (const auto& pair : _uiview_keyevt_callbacks)
		if (this == pair.first)
			_uiview_keyevt_callbacks.erase(pair.first);
}

void UIView::SetSourceWindow(UIWindow* window)
{
	this->sourceWindow = window;
}

void UIView::ProcessScrollWheelAction(short scroll_delta)
{
	eventManager->ParseScrollWheelsEvent(scroll_delta);
}

void UIView::MouseClickEventListener()
{
	bool lmClicked = false;
	bool rmClicked = false;

	while (!_should_terminate)
	{
		Sleep(2);
		if (sourceWindow == nullptr) continue;

		// checking if buttons are not clicked anymore
		if(GetAsyncKeyState(VK_LBUTTON) == 0) lmClicked = false;
		if(GetAsyncKeyState(VK_RBUTTON) == 0) rmClicked = false;

		if (GetAsyncKeyState(VK_LBUTTON) & 0x8000 && !lmClicked)
		{
			lmClicked = true;
			POINT pos;
			pos.x = (LONG)Mouse::GetMousePosX(sourceWindow);
			pos.y = (LONG)Mouse::GetMousePosY(sourceWindow);
			if (pos.x > sourceWindow->GetWidth() || pos.x < 0 || pos.y > sourceWindow->GetHeight() || pos.y < 0) continue;

			eventManager->ParseMouseClickEvent(pos, LeftClick);
		}

		if (GetAsyncKeyState(VK_RBUTTON) & 0x8000 && !rmClicked)
		{
			rmClicked = true;
			POINT pos;
			pos.x = (LONG)Mouse::GetMousePosX(sourceWindow);
			pos.y = (LONG)Mouse::GetMousePosY(sourceWindow);
			if (pos.x > sourceWindow->GetWidth() || pos.x < 0 || pos.y > sourceWindow->GetHeight() || pos.y < 0) continue;

			eventManager->ParseMouseClickEvent(pos, RightClick);
		}
	}

	_mouse_click_event_listener_terminated = true;
}

void UIView::MouseMovementEventListener()
{
	POINT previousMousePosition;
	previousMousePosition.x = (LONG)Mouse::GetMousePosX(sourceWindow);
	previousMousePosition.y = (LONG)Mouse::GetMousePosY(sourceWindow);

	// hover on/off events
	UIElement* previousActiveElement = nullptr;

	while (!_should_terminate)
	{
		Sleep(2);
		if (sourceWindow == nullptr) continue;

		POINT currentMousePosition;
		currentMousePosition.x = (LONG)Mouse::GetMousePosX(sourceWindow);
		currentMousePosition.y = (LONG)Mouse::GetMousePosY(sourceWindow);
		// if mouse is not in the window
		if (currentMousePosition.x > sourceWindow->GetWidth() || currentMousePosition.x < 0 ||  currentMousePosition.y > sourceWindow->GetHeight() || currentMousePosition.y < 0) continue;

		if (currentMousePosition.x != previousMousePosition.x || currentMousePosition.y != previousMousePosition.y)
		{
			// mouse moved
			eventManager->ParseMouseMovedEvent(currentMousePosition, previousActiveElement);
		}

		previousMousePosition = currentMousePosition;
	}

	_mouse_movement_event_listener_terminated = true;
}

void UIView::MousePressedEventListener()
{
	bool mouseDown = false;
	bool mouseAlreadyDownOnElement = false;
	UIElement* previousActiveElement = nullptr;

	while (!_should_terminate)
	{
		Sleep(2);
		if (sourceWindow == nullptr) continue;

		// checking if mouse is not pressed anymore
		if (GetAsyncKeyState(VK_LBUTTON) == 0)
		{
			mouseDown = false;
			mouseAlreadyDownOnElement = false;
		}

		if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
		{
			POINT pos;
			pos.x = (LONG)Mouse::GetMousePosX(sourceWindow);
			pos.y = (LONG)Mouse::GetMousePosY(sourceWindow);
			if (pos.x > sourceWindow->GetWidth() || pos.x < 0 || pos.y > sourceWindow->GetHeight() || pos.y < 0) continue;

			UIElement* targetElement = nullptr;
			eventManager->ParseMouseDownEvent(pos, mouseAlreadyDownOnElement, previousActiveElement, targetElement);
			
			if (!mouseDown)
			{
				previousActiveElement = targetElement;
				mouseAlreadyDownOnElement = true;
			}

			mouseDown = true;
		}
	}

	_mouse_pressed_event_listener_terminated = true;
}

void UIView::ProcessKeyEvents(int key, int state)
{
	if (_should_terminate) return;

	KeyEvt _event;
	_event.key = Keyboard::GetKeyValue(key, (Keyboard::IsKeyDown(MC_LSHIFT) || Keyboard::IsKeyDown(MC_RSHIFT)), (GetKeyState(VK_CAPITAL) & 0x0001));
	_event.code = key;
	_event.event_type = state;
	eventManager->ParseKeypress(_event);
}
